#  StormMUD - A multiplayer combat game - http://stormmud.david-c-brown.com
#  Copyright (C) 2009, 2010 - David C Brown & Mark Richardson
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.

from twisted.conch.telnet import StatefulTelnetProtocol

import smConfig, smParser, smLog

from smDefines import DELETELEFT, FIRSTCOL, SAVECUR, RESTORECUR
from smDefines import PLAYING
from smDefines import WHITE, LGREEN, BLUE, LRED
from smDefines import INFO, DEBUG, WARN, ERROR, FILE, CONSOLE
from smMaps import WORLD, GetRoomByID
from smCommands import LookRoom

from time import strftime, localtime

AllPlayers            = {}
PlayersInGame         = {}


# Wear locations for player's WEARING attribute (hash key).
HEAD                  =  1
EARS                  =  2
NECK                  =  3
TORSO                 =  4
BACK                  =  5
ARMS                  =  6
HANDS                 =  7
RFINGER               =  8
LFINGER               =  9
WEAPON                = 10
OFFHAND               = 11
PANTS                 = 12
FEET                  = 13
WIELDED               = 14

X                     = 1
MAXCMDS               = 8


class Player(StatefulTelnetProtocol):
    """The player class.  It holds all the attibutes the player
       needs for the game"""

    def __init__(self):
        """Player -> __init__(): Initialize the Player class when created"""

        # Personal attributes
        self.id                   = 0
        self.name                 = "" # Also serves as the players player ID
        self.surname              = ""
        self.password             = ""
        self.SuicidePassword      = ""
        self.GuildID              = 0
        self.PersonalDesc         = ""
        self.BroadcastChannel     = 0
        self.room                 = ""

        # Player Stats
        self.HP                   = 0
        self.MaxHP                = 0
        self.mana                 = 0
        self.MaxMana              = 0
        self.bank                 = 0
        self.offense              = 0
        self.defense              = 0
        self.strength             = 0
        self.agility              = 0
        self.intellect            = 0
        self.wisdom               = 0
        self.consitution          = 0
        self.charisma             = 0
        self.AttackSpeed          = 0
        self.preception           = 0
        self.stealth              = 0
        self.SpellCasting         = 0
        self.MagicRes             = 0
        self.SavingsThrow         = 0
        self.ArmorClass           = 0
        self.experience           = 0
        self.vision               = 0
        self.hunger               = 0
        self.ResistHot            = 0
        self.ResistCold           = 0
        self.ResistAir            = 0
        self.ResistLightning      = 0
        self.ResistEarth          = 0
        self.ResistPsionics       = 0
        self.traps                = 0
        self.tracking             = 0
        self.PlayerClass          = 0
        self.PlayerRace           = 0
        self.AttackSpeed          = 0
        self.CriticalChance       = 0
        self.BackstabBonus        = 0
        self.PlayingStatus        = None
        self.theivery             = 0
        self.picklocks            = 0
        self.forage               = 0
        self.alchemy              = 0
        self.cooking              = 0
        self.forging              = 0
        self.leathermaking        = 0
        self.woodworking          = 0
        self.AdminLevel           = 0
        self.encumberance         = 0
        self.status               = 0

        # Player flags: True or False only
        self.sneaking             = False
        self.paralyzed            = False
        self.held                 = False
        self.stun                 = False
        self.telepaths            = False
        self.gossips              = False
        self.gangpaths            = False
        self.partypaths           = False
        self.moving               = False
        self.blind                = False
        self.BriefDesc            = False
        self.IsBackstabbing       = False
        self.resting              = False

        # Container attributes
        self.spells               = {}
        self.items                = [] # See attribute document
        self.money                = {}
        self.BankVault            = [] # See attribute document
        self.quests               = {}
        self.wearing              = {}
        self.CmdStack             = []


    def connectionMade(self):
        global X, logger

        """Player -> connectionMade(): (overridden from Twisted) is called when someone connects"""

        # Limit how many can connect at one time
        if len(AllPlayers) > smConfig.GameConfig.MaxPlayers:
            self.transport.write("Too many connections, try later")
            self.disconnectClient()

        smLog.logger.Logit(INFO, self.transport.getPeer().host + " CONNECTED!")
        self.name = "Player%i" % (X)
        self.room = "MAIN|1"
        self.sendToRoom("%s has joined." % self.name)
        AllPlayers[self.name] = self
        PlayersInGame[self.name] = self
        
        WORLD['MAIN'].rooms['MAIN|1'].PlayersInRoom[self.name] = self
        X += 1
        LookRoom(self, self.room)


    def disconnectClient(self):
        """Player -> disconnectClient(): (overridden from Twisted) is called when someone quits properly"""

        self.sendLine("Goodbye")
        if AllPlayers.has_key(self.name):
            self.RemovePlayerFromGame()

            #self.factory.sendMessageToAllClients(amDefines.BLUE + self.name + " just logged off.")
            smLog.logger.Logit(INFO, strftime("%b %d %Y %H:%M:%S", localtime()) + self.name + " just logged off.")
            self.sendToRoom("%s logged off." % self.name)
        self.transport.loseConnection()


    def connectionLost( self, reason ):
        """Player -> connectionLost(): (overridden from Twisted) is called when a player disconnects in a
           way other than intentional:"""

        # If player hungup, disconnectClient() didn't remove the user, remove them now
        if AllPlayers.has_key(self.name):
            self.RemovePlayerFromGame()

            #self.factory.sendMessageToAllClients(amDefines.BLUE + self.name + " just hung up!")
            smLog.logger.Logit(INFO,  strftime("%b %d %Y %H:%M:%S ", localtime()) + self.name + " just hung up!")
            self.sendToRoom("%s just hung up!" % self.name)


    def RemovePlayerFromGame( self ):
        """Player -> RemovePlayerFromGame(): Remove the player from the game, this can be called from
           being kicked or someone quitting"""

        del AllPlayers[self.name]
        # If player was playing, remove them
        if self.status == 0:
            del PlayersInGame[self.name]
        if self.room:
            Room = GetRoomByID(self.room)
            del Room.PlayersInRoom[self.name]

    # Accept user input and direct it to the parser.
    def lineReceived(self, line):
        """Player -> lineReceived(): This function accepts user input and dispatches it to the parsers"""

        smParser.GameParser(self, line)

    def sendToPlayer(self, line):
        """Player -> sendToPlayer(): Sends text to a player"""

        self.transport.write(DELETELEFT)
        self.transport.write(FIRSTCOL)
        self.sendLine(line + WHITE)
        self.DisplayStatLine()

    #===============================================
    # Send to everyone in current room but player
    #===============================================
    def sendToRoom(self, line):
        """Player -> sendToRoom(): Sends text to everyone in the room except the player.obj sending it"""

        Room = GetRoomByID(self.room)
        for _player in Room.PlayersInRoom.values():
            if _player == self:
                pass
            else:
                _player.transport.write(DELETELEFT)
                _player.transport.write(FIRSTCOL)
                _player.sendToPlayer(line + WHITE)
                _player.DisplayStatLine()


    def IsWearing(self, itemname, location):
        """
        Player->IsWearing()
        Is the player wearing said item
        """
        
        if location == None:
            itemstr = re.compile(re.escape(itemname.lower()))
            for worn in self.wearing.values():
                namelist = worn.name.split()
                if itemstr.match(namelist):
                    return True
            
    #===============================================
    # Player->AddCmdToStack()
    #===============================================
    def AddCmdToStack(self, cmd):
        """
        Player->AddCmdToStack()
        Adds a command to the players command stack.
        If player is moving, players command won't 
        execute until after the move has occured. CmdStack
        is the stack where the commands get stored until ready.
        """
        if len(self.CmdStack) > MAXCMDS:
            self.sendToPlayer("Slow down, you are sending to many commands!")
        else:
            self.CmdStack.insert(0, cmd)
        
        return None
    
    #===============================================
    # PopCmdStack()
    #===============================================
    def PopCmdStack(self):
        """
        Player->PopCmdStack()
        Loops through the stacked player commands
        executing them as long as the player isn't
        moving.
        """

        while not self.moving:
            if len(self.CmdStack) > 0:
                smParser.GameParser(self, self.CmdStack.pop())
            else:
                return
            
            
            
    #================================================
    # Player->DisplayStatLine()
    #================================================
    def DisplayStatLine(self):
        def SetStatColor(stat, MaxStat):
            if stat < ( ( float(MaxStat) / 100) * 25 ):
                return LRED
            elif stat < ( ( float(MaxStat) / 100) * 50 ):
                return YELLOW
            elif stat < ( ( float(MaxStat) / 100) * 75 ):
                return LGREEN
            elif stat < ( ( float(MaxStat) / 100) * 85 ):
                return WHITE
            elif stat < ( ( float(MaxStat) / 100) * 95 ):
                return WHITE
            elif stat > MaxStat:
                return BLUE
            else:
                return WHITE
            
        # If player not playing, return
        #if self.STATUS != PLAYING:
        #    return
        
        hpcolor   = SetStatColor(self.HP, self.MaxHP)
        manacolor = SetStatColor(self.mana, self.MaxMana)
        
        if self.MaxMana > 0:
            STATLINE = "[HP=%s%d%s/%d MA=%s%d%s/%d]: " % (hpcolor, self.HP, WHITE, self.MaxHP, manacolor, self.mana, WHITE, self.MaxMana)
        else:
            STATLINE = "[HP=%s%d%s/%d]: " % (hpcolor, self.HP, WHITE, self.MaxHP)
                      
        if self.resting:
            STATLINE += "(resting) " 

        STATSIZE = len(STATLINE)
        self.transport.write(SAVECUR)
        self.transport.write(FIRSTCOL)
        self.transport.write(STATLINE)
        self.transport.write(RESTORECUR)
            
            